Instalación y/o carga del paquete CDR
if (!require(CDR)){
if (!require(remotes)) {install.packages("remotes")}
remotes::install_github("cdr-book/CDR")
}Los datos que se utilizan en esta historia están disponibles en el paquete CDR que puede instalarse con el siguiente comando (se comprueba si no lo está):
CDRif (!require(CDR)){
if (!require(remotes)) {install.packages("remotes")}
remotes::install_github("cdr-book/CDR")
}Son datos abiertos proporcionados por el Portal de datos abiertos del Ayuntamiento de Madrid. Concretamente, los facilitados por el Sistema Integral de la Calidad del Aire del Ayuntamiento de Madrid, que pone a disposición los datos de los contaminantes registrados por las estaciones de medición situadas en Madrid. Los datos son de frecuencia horaria por anualidades desde 2001 y se actualizan de forma mensual.
Mi primo se ha vuelto un radical: le ha dado por coger la bicicleta y quiere aprobar una oposición en el Ayuntamiento de Madrid porque “va a acabar con tanto coche en esta ciudad tan contaminada” [sic]. A ver, que yo lo entiendo, ¿eh? Es que el pobre tiene alergias de todas clases, pero las peores las tiene a los gases contaminantes, y ha llegado a una situación muy desesperada… ¡Da una pena!
Así que me he puesto a pensar y claro, por mi primo del alma tengo que hacer algo… Voy a ver si encuentro un sitio donde pueda vivir algo más despejado de contaminación mientras aprueba la oposición, sube en la carrera administrativa, llega a un puesto de responsabilidad y puede hacer algo por el asunto… porque hasta que lo consiga…
Es crucial proporcionar el contexto adecuado para que los lectores comprendan de dónde provienen los datos y por qué son relevantes.
Además, tenemos que saber muy bien qué es lo que queremos obtener como producto, en este caso para mi primo al que quiero ser capaz de localizarle sitios con menos contaminación.
Estamos buscando un lugar, por lo que vamos a tratar los datos espacialmente.
estaciones id id_name longitud latitud nom_mag nom_abv
1 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
2 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
3 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
4 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
5 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
6 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
ud_med fecha daily_mean zona tipo
1 µg/m3 2011-01-01 0.9375000 Interior M30 Tráfico
2 µg/m3 2011-01-02 1.0666667 Interior M30 Tráfico
3 µg/m3 2011-01-03 1.6666667 Interior M30 Tráfico
4 µg/m3 2011-01-04 1.1416667 Interior M30 Tráfico
5 µg/m3 2011-01-05 1.0416667 Interior M30 Tráfico
6 µg/m3 2011-01-06 0.4166667 Interior M30 Tráfico
str(contam_mad)Classes 'data.table' and 'data.frame': 521388 obs. of 12 variables:
$ estaciones: chr "E08: Escuelas Aguirre" "E08: Escuelas Aguirre" "E08: Escuelas Aguirre" "E08: Escuelas Aguirre" ...
$ id : chr "E08" "E08" "E08" "E08" ...
$ id_name : chr "Escuelas Aguirre" "Escuelas Aguirre" "Escuelas Aguirre" "Escuelas Aguirre" ...
$ longitud : num -3.68 -3.68 -3.68 -3.68 -3.68 ...
$ latitud : num 40.4 40.4 40.4 40.4 40.4 ...
$ nom_mag : chr "Benceno" "Benceno" "Benceno" "Benceno" ...
$ nom_abv : chr "BEN" "BEN" "BEN" "BEN" ...
$ ud_med : chr "µg/m3" "µg/m3" "µg/m3" "µg/m3" ...
$ fecha : Date, format: "2011-01-01" "2011-01-02" ...
$ daily_mean: num 0.938 1.067 1.667 1.142 1.042 ...
$ zona : chr "Interior M30" "Interior M30" "Interior M30" "Interior M30" ...
$ tipo : chr "Tráfico" "Tráfico" "Tráfico" "Tráfico" ...
- attr(*, "sorted")= chr [1:9] "estaciones" "id" "id_name" "longitud" ...
- attr(*, ".internal.selfref")=<externalptr>
datatable(contam_mad[1:5, ])# Ver los primeros y últimos registros:
head(contam_mad, 3) estaciones id id_name longitud latitud nom_mag nom_abv
1 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
2 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
3 E08: Escuelas Aguirre E08 Escuelas Aguirre -3.682316 40.42155 Benceno BEN
ud_med fecha daily_mean zona tipo
1 µg/m3 2011-01-01 0.937500 Interior M30 Tráfico
2 µg/m3 2011-01-02 1.066667 Interior M30 Tráfico
3 µg/m3 2011-01-03 1.666667 Interior M30 Tráfico
tail(contam_mad, 3) estaciones id id_name longitud latitud nom_mag
521386 E60: Tres Olivos E60 Tres Olivos -3.689731 40.50055 Óxidos de Nitrógeno
521387 E60: Tres Olivos E60 Tres Olivos -3.689731 40.50055 Óxidos de Nitrógeno
521388 E60: Tres Olivos E60 Tres Olivos -3.689731 40.50055 Óxidos de Nitrógeno
nom_abv ud_med fecha daily_mean zona tipo
521386 NOx µg/m3 2022-04-28 32.91667 Noreste Fondo
521387 NOx µg/m3 2022-04-29 23.83333 Noreste Fondo
521388 NOx µg/m3 2022-04-30 35.04167 Noreste Fondo
# Ver la dimensión de la tabla de datos
dim(contam_mad)[1] 521388 12
Los conjuntos de datos de la calidad de aire son complejos y en algunos casos los datos no pueden utilizarse tal cual y pueden requerir un pre-procesamiento cuidadoso antes de llegar a cualquier conclusión. Debe prestarse atención a la existencia de subgrupos.
Ejercicio 1 Vamos a empezar por el NOx… ¿Cuál es el día con mayor y menor concentración de NOx de todo el periodo?
contam_mad |> # Summary por grupo usando dplyr
na.omit() |> # omitimos los NAs para el análisis
filter(nom_abv == "NOx") |> # filtramos por NOx
group_by(fecha) |> # agrupamos por fecha
summarize(mad_mean = mean(daily_mean)) |> # promedio de las estaciones
slice(which.max(mad_mean), which.min(mad_mean)) # seleccionamos el máximo y el mínimo# A tibble: 2 × 2
fecha mad_mean
<date> <dbl>
1 2011-12-21 415.
2 2020-05-10 6.33
El valor máximo, 415,48 µg/m3 de NOx, se observa el 21 de diciembre de 2011 y el valor mínimo, 6,32 µg/m3 de NOx, el 10 de mayo de 2020, en pleno estado de alarma.
Ejercicio 2 ¿Cómo son los datos de PM2.5 en la ciudad de Madrid?
contam_mad |> # Summary por grupo usando dplyr
na.omit() |> # omitimos los NAs para el análisis
filter(nom_abv == "PM2.5") |> # filtramos por PM2.5
group_by(id_name) |>
summarize(
min = min(daily_mean),
q1 = quantile(daily_mean, 0.25),
median = median(daily_mean),
mean = mean(daily_mean),
q3 = quantile(daily_mean, 0.75),
max = max(daily_mean)
)# A tibble: 8 × 7
id_name min q1 median mean q3 max
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Casa de Campo 0.583 5.33 8.08 9.28 11.8 177
2 Castellana 0.125 6.08 8.83 9.96 12.3 109.
3 Cuatro Caminos 1 6.54 9.5 10.7 13.4 59.2
4 Escuelas Aguirre 0.0417 7.25 10.3 11.5 14.3 108.
5 Mendez Alvaro 0.0417 6.17 9.42 10.7 13.8 78.1
6 Plaza Elíptica 1.21 6.42 10.1 11.2 14 148.
7 Plaza de Castilla 0.0417 6.29 8.92 10.1 12.1 198.
8 Sanchinarro 1 4.47 7.27 8.60 10.5 68.5
Ya hemos visto paquetes como skimr, DataExplorer (hay más: dlookr…) que generan resúmenes exploratorios automáticos con los principales descriptivos. Son muy útiles y merece la pena conocerlos, porque vemos la estructura de los datos y la relación entre las variables mucho más deprisa.
Ejercicio 3 Con las funciones del paquete tidyverse representa la evolución de todos los contaminantes medidos por las estaciones de monitoreo de la ciudad de Madrid en el periodo 2011-2020.
plot_contam_mad <- contam_mad |>
group_by(fecha, nom_mag) |>
summarise(media_estaciones = mean(daily_mean, na.rm = TRUE)) |>
ggplot(aes(x = fecha, y = media_estaciones)) +
geom_line() +
geom_smooth() +
labs(x = "año", y = "media de las estaciones") +
theme_minimal() +
facet_wrap(~nom_mag, scales = "free_y")
plot_contam_madVamos a mejorar el gráfico anterior para que sea más legible y efectivo.
Completa las partes del código señaladas por _____ o xxxxx para obtener el resultado propuesto.
contam_mad |>
group_by(semana = floor_date(fecha, unit = "_____"), nom_mag) |>
summarise(media_estaciones = mean(_______, na.rm = TRUE)) |>
ggplot(aes(x = semana, y = media_estaciones)) +
geom_xxxxx(aes(color = nom_mag)) +
geom_xxxxx(size = 0.5, color = "black") +
scale_color_brewer(palette = "Paired") +
labs(
x = NULL,
y = "(µg/m3)",
______ = "Evolución de partículas contaminantes en Madrid",
______ = "La concentración media semanal disminuye desde 2011 en la mayoría de contaminantes",
______ = "Fuente: Portal de datos abiertos del Ayuntamiento de Madrid"
) +
theme_xxxxx() +
theme(legend.position = "none") +
facet_wrap(~nom_mag, scales = "free_y")Aquí hemos visto que el color, aunque no es imprescindible para dar información en nuestra visualización, consigue una estética más agradable.
Lo que está claro es que los contaminantes con mayor concentración son los óxidos de nitrógeno y las partículas <10µm.
Antes de continuar, no nos olvidemos de los datos faltantes, a veces un importante problema en ciencia de datos… ¿Son muchos? ¿Existe algún patrón en los NAs? ¿Están asociados a una o más variables?
Ejercicio 4 ¿Cuántos NAs tengo en mi conjunto de datos contam_mad?
Ejercicio 5 ¿Puedo visualizar dónde están los datos faltantes por estación de monitoreo a lo largo del tiempo?
na_table <- contam_mad |>
mutate(isna = is.na(daily_mean)) |>
ggplot(aes(x = fecha, y = id_name, fill = isna)) +
geom_raster() +
scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "lightblue"))
na_tableEjercicio 6 ¿Qué hacemos con los NAs?
Opción 1: Eliminar los NAs
estaciones id id_name longitud
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:505773 FALSE:505773 FALSE:505773 FALSE:505773
latitud nom_mag nom_abv ud_med
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:505773 FALSE:505773 FALSE:505773 FALSE:505773
fecha daily_mean zona tipo
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:505773 FALSE:505773 FALSE:505773 FALSE:505773
Opción 2: Imputar NAs con el día anterior
estaciones id id_name longitud
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:521388 FALSE:521388 FALSE:521388 FALSE:521388
latitud nom_mag nom_abv ud_med
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:521388 FALSE:521388 FALSE:521388 FALSE:521388
fecha daily_mean zona tipo
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:521388 FALSE:521388 FALSE:521388 FALSE:521388
Completa las partes del código señaladas por ______ o xxx para obtener el resultado esperado.
summary(____(contam_mad_mediana)) estaciones id id_name longitud
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:521388 FALSE:521388 FALSE:521388 FALSE:521388
latitud nom_mag nom_abv ud_med
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:521388 FALSE:521388 FALSE:521388 FALSE:521388
fecha daily_mean zona tipo
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:521388 FALSE:521388 FALSE:521388 FALSE:521388
Mantén el código limpio y organizado para facilitar su comprensión y mantenimiento. Veamos un ejemplo con el siguiente ejemplo. Una herramienta muy útil para tener una visión general de estos contaminantes es viendo el calendario como un heatmap: calendar heatmap
Preparamos un código para visualizar la concentración media de los contaminantes en Madrid a lo largo del tiempo en forma de calendario que pueda ser utilizado para cualquier contaminante.
calendar_plot <- contam_mad |>
group_by(fecha, nom_mag, nom_abv, ud_med) |>
summarize(valor_promedio = mean(daily_mean, na.rm = T))
# Dates as factors
months <-
seq.Date(
from = as.Date("2022-01-01"),
length.out = 12,
by = "month"
) |> format("%B")
wdays <-
seq.Date(
from = as.Date("2022-05-30"),
length.out = 7,
by = "day"
) |> format("%A")
calendar_plot <- calendar_plot |>
mutate(
year = format(fecha, "%Y"),
month = factor(format(fecha, "%B"), levels = months, labels = months),
wday = factor(weekdays(fecha), levels = wdays, labels = wdays),
week = as.numeric(format(fecha, "%W"))
)
calendar_plot <- calendar_plot |>
group_by(year, month) |>
mutate(wmonth = 1 + week - min(week))Ejercicio 7 Calendar heatmap para el NOx
i_mag <- "Óxidos de Nitrógeno" # Seleccionar el contaminante
fill_title <- calendar_plot |>
filter(nom_mag == i_mag & year >= 2011) |>
ungroup() |>
distinct(paste(unique(nom_abv), unique(ud_med)))
calendar_plot |>
filter(nom_mag == i_mag & year >= 2011) |>
ggplot(aes(
x = wmonth,
y = reorder(wday, -as.numeric(wday)),
fill = valor_promedio
)) +
geom_tile(colour = "white") +
facet_grid(year ~ month) +
scale_fill_gradient(low = "yellow", high = "red", ) +
scale_x_continuous(breaks = 1:5, limits = c(0, 6)) +
labs(
x = "Semana del mes",
y = NULL,
title = paste0("Concentración de ", i_mag, " por día de la semana"),
fill = fill_title,
caption = "Fuente: Red de Vigilancia de la Calidad del Aire del Ayto. de Madrid"
)Ya le puedo decir algo a mi primo: cuidado con el NOx en los meses de invierno especialmente. Vamos a ver el otro contaminante importante de Madrid: las partículas menores de 10 µm.
Pufff, pues en las partículas <10µm no está tan definida la época de mayor contaminación…
Completa las partes del código señaladas por _____ o xxxxx para obtener el calendario de PM10.
i_mag <- "_______________"
fill_title <- calendar_plot |>
filter(nom_mag == i_mag & year >= 2011) |>
ungroup() |>
distinct(paste(unique(nom_abv), unique(ud_med)))
calendar_plot |>
filter(nom_mag == i_mag & year >= 2011) |>
ggplot(aes(
x = wmonth,
y = reorder(wday, -as.numeric(wday)),
fill = valor_promedio
)) +
geom_tile(colour = "white") +
facet_grid(year ~ month) +
scale_fill_gradient(low = "________", high = "red", ) +
scale_x_continuous(breaks = 1:5, limits = c(0, 6)) +
labs(
x = "Semana del mes",
y = NULL,
title = paste0("Concentración de ", i_mag, " por día de la semana"),
fill = fill_title,
caption = "Fuente: Red de Vigilancia de la Calidad del Aire del Ayto. de Madrid"
)Ejercicio 8 Nos centramos en los dos contaminantes más problemáticos en la ciudad de Madrid (PM10 y NOx)
Completa las partes del código señaladas por _____ o xxxxx para obtener el resultado deseado.
plot_pm10_nox <- ____________ |>
group_by(fecha, nom_mag) |>
summarise(media_estaciones = mean(daily_mean, na.rm = TRUE)) |>
ggplot(aes(x = ___________, y = ____________)) +
geom_line(aes(color = nom_mag)) +
labs(
x = NULL, y = "(µg/m3)",
title = "Evolución semanal de PM10 y NOx en Madrid",
subtitle = "Concentración media semanal en las estaciones de medición",
caption = "Fuente: Portal de datos abiertos del Ayuntamiento de Madrid"
) +
theme_bw() +
theme(legend.position = "none") +
facet_wrap(~nom_mag, scales = "free_y", ncol = 1)
plot_pm10_noxEjercicio 9 ¿Por qué no hacer el anterior gráfico interactivo? Le facilitaríamos mucho las cosas a mi primo…
La función ggplotly() de la librería plotly permite hacer fácilmente gráficos interactivos.
plotly::ggplotly(plot_pm10_nox)Ejercicio 10 ¿Habrá algún tipo de relación entre los dos contaminantes? Vamos a ver si encontramos alguna durante el estado de alarma por la pandemia de COVID-19.
pm10_nox_mad_alarma <- contam_mad |>
na.omit() |>
filter(nom_abv %in% c("PM10", "NOx")) |>
# período del estado de alarma
filter(between(fecha, left = as.Date("2020-03-14"), right = as.Date("2020-06-30"))) |>
select(estaciones, zona, tipo, nom_abv, daily_mean, fecha) |>
pivot_wider(names_from = "nom_abv", values_from = "daily_mean", values_fn = mean)
pm10_nox_mad_alarma |>
ggplot(
aes(x = PM10, y = NOx, colour = tipo, size = zona)
) +
geom_point()pm10_nox_mad_alarma |>
drop_na() |>
ggplot(aes(y = NOx, x = PM10, colour = tipo, shape = zona)) +
geom_point() +
geom_smooth() +
facet_wrap(vars(estaciones))A ver, que igual hay alguna relación, pero no se ve claramente, y hay diferencias entre estaciones, por lo que no creo que sea de ayuda a mi primo. Nos centraremos en el NOx, a ver si veo algo más claro.
Completa las partes del código señaladas por _____ o xxxxx para obtener el resultado deseado.
nox_madrid |>
_____(aes(x= zona, y=daily_mean)) +
geom_xxxxx() +
geom_xxxxx(height = 0, width = 0.01) +
aes(x = zona, y = daily_mean, fill = zona) +
labs(
title = "Distribución de NOx por zona",
x = "Zona",
y = "Concentración de NOx (µg/m3)"
) +
theme_xxxxx()¡Esto ya es otra cosa! En cuanto hemos introducido la dimensión espacial, tenemos resultados interesantes. Vamos a sacar algunas estadísticas:
Completa las partes del código señaladas por _____ o xxxxx para obtener el resultado deseado.
ggbetweenstats(
data = ______,
x = _______,
y = _______
)Ahora, utilizando la función geom_density_ridges() de la librería ggridges vamos a hacer un gráfico de densidad (Ridgeline) de NOx por zona.
nox_madrid |>
ggplot(aes(x = daily_mean, y = zona, fill = zona)) +
geom_density_ridges() +
theme_ridges() +
labs(
title = "Distribución de NOx por zona de calidad del aire",
x = "Concentración de NOx (µg/m3)",
y = "Zona"
) +
theme_minimal()Igual que con los violines, se aprecia muy bien que la zona con menos concentración es el noroeste…
Ejercicio 11 ¿Cómo es la relación bivariante de estos dos contaminantes?
Comprueba con un Análisis de la Varianza si los niveles de concentración de NOx dependen de las variables tipo y zona
Df Sum Sq Mean Sq F value Pr(>F)
zona 4 22508217 5627054 1540.1 <2e-16 ***
tipo 2 5489043 2744521 751.2 <2e-16 ***
Residuals 94975 347009104 3654
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Tanto la zona como el tipo de contaminante son significativos, lo que ya a priori tenía sentido. Vamos a representarlo, a ver si lo vemos mejor que con tanto número:
ggplot(contam_mad_anova, aes(x = zona, y = daily_mean, fill = tipo)) +
geom_boxplot() +
labs(title = "NOx por Zona y Tipo",
x = "Zona",
y = "Daily Mean",
fill = "Tipo") +
theme_minimal()Me estoy acordando de la calima de marzo de 2022, a mi primo casi le da algo. Voy a ver si le puedo dar alguna pista. En este caso es evidente que la subida en situaciones de calima es por las partículas pequeñas, por lo que nos centraremos en ellas.
¿Qué paso la semana de calima de marzo de 2022 con las partículas <2.5µm y <10µm en la ciudad de Madrid?
particulas <- c("Partículas < 2.5 µm", "Partículas < 10 µm")
calima <- contam_mad |>
filter(nom_mag %in% particulas &
fecha %in% seq.Date(as.Date("2022-03-01"), by = "day", length.out = 31)) |>
group_by(fecha, id, id_name, nom_mag, nom_abv, ud_med) |>
summarize(valor_promedio = mean(daily_mean, na.rm = T))
max_2.5 <- calima |>
ungroup() |>
filter(nom_mag == particulas[1]) |>
slice(which.max(valor_promedio))
max_10 <- calima |>
ungroup() |>
filter(nom_mag == particulas[2]) |>
slice(which.max(valor_promedio))
calima |>
ggplot(aes(fecha, valor_promedio, colour = nom_mag)) +
geom_jitter() +
geom_smooth(
method = "loess",
span = .5,
se = FALSE,
show.legend = FALSE
) +
scale_x_date(
breaks = seq.Date(as.Date("2022-03-01"), by = "week", length.out = 5),
date_labels = "%d-%b"
) +
scale_color_manual(values = c("#261606", "#DD9C4A")) +
geom_label_repel(
data = max_10,
mapping = aes(fecha, valor_promedio, label = paste(id_name)),
show.legend = FALSE
) +
geom_label_repel(
data = max_2.5,
mapping = aes(fecha, valor_promedio, label = paste(id_name)),
show.legend = FALSE
) +
labs(
title = "Registro de partículas durante el mes de marzo 2022",
subtitle = "Madrid",
x = NULL,
y = unique(calima$ud_med),
color = NULL,
caption = "Fuente: Red de Vigilancia de la Calidad del Aire del Ayto. de Madrid"
) +
theme_minimal()Para que flipe mi primo, le voy a hacer una animación para que vea cómo varió la concentración del PM10 durante la calima (del 13 al 17 de marzo).
mad_sf <- esp_get_munic(munic = "^Madrid$", epsg = 4326)
marzo_pm10 <- contam_mad |>
filter(nom_abv == "PM10" & fecha >= as.Date("2022-03-13") & fecha <= as.Date("2022-03-17")) |>
drop_na()
madrid_estaciones_sf <- st_as_sf(marzo_pm10,
coords = c("longitud", "latitud"),
crs = 4326
)
ggplot(madrid_estaciones_sf) +
geom_sf(
data = mad_sf,
fill = "#DD9C4A",
) +
geom_sf(aes(fill = daily_mean),
shape = 21,
size = 5,
alpha = .7
) +
labs(fill = "PM10") +
scale_fill_viridis_c(direction = -1) +
theme_void() +
labs(
title = "PM10: {current_frame}"
) +
transition_manual(fecha) +
ease_aes("linear") +
theme(
plot.title = element_text(
size = 12,
face = "bold"
),
plot.subtitle = element_text(
size = 8,
face = "italic"
)
)¿Cuál ha sido el planteamiento? Como siempre, proporcionamos el contexto: mi primo es alérgico y le vendría saber dónde hay menos contaminación, para que viva mejor.
El análisis exploratorio nos introduce de lleno en la trama, donde descubrimos que hay alguna relación entre contaminantes, pero él no está interesado en esta parte de nuestro análisis. A nuestro personaje le interesan concentraciones, periodos de contaminación y lugares.
Pero como siempre, lo más importante es el desenlace, donde le estamos dando a nuestro querido primo la información que necesita:
Con el gráfico de concentración de contaminantes, va a saber que lo que tiene que vigilar son los óxidos de nitrógeno y las micropartículas.
Con el gráfico de violines por zonas, va a saber que la mejor para un alérgico es el noroeste de Madrid.
También podemos enseñarle el gráfico de ´micropartículas, que le servirá para tomar precauciones durante períodos de calima.